package com.sc.core.make.file;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.annotation.TableName;
import com.sc.common.entity.AbstractBaseEntity;
import com.sc.common.entity.IEnum;
import com.sc.core.make.MasterMake;
import com.sc.core.make.build.*;
import com.sc.core.make.dto.MakeInfoDTO;
import com.sc.core.make.enums.ClassType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;

import java.io.File;
import java.io.IOException;
import java.io.Serial;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Log4j2
public class Entity implements IFileMake{

    private final MakeInfoDTO makeInfo;

    private final Map<String, Map<Integer, String>> enumMap;
    private final Map<String, String> translation;

    @Getter
    private String message = "成功";

    public Entity(MakeInfoDTO makeInfo, Map<String, Map<Integer, String>> enumMap, Map<String, String> translation) {
        this.makeInfo = makeInfo;
        this.enumMap = enumMap;
        this.translation = translation;
    }

    public Entity make() {
        ClassBuild entityBuild = new ClassBuild(makeInfo.getClassname() + "Entity");

        entityBuild.setPackageName(makeInfo.getPackageName());
        entityBuild.setDocument(new String[]{makeInfo.getTableComment()});
        entityBuild.addImportClass(Data.class);
        entityBuild.addImportClass(TableName.class);
        entityBuild.addImportClass(EqualsAndHashCode.class);
        entityBuild.addAnnotationClass(new AnnotationBuild(Data.class));
        entityBuild.addAnnotationClass(new AnnotationBuild(TableName.class).addParam("value", makeInfo.getTableName()));
        entityBuild.addAnnotationClass(new AnnotationBuild(EqualsAndHashCode.class).addParam("callSuper", true));

        entityBuild.addImportClass(AbstractBaseEntity.class.getCanonicalName());
        entityBuild.setExtendClass(new GenericTypeBuild(AbstractBaseEntity.class));

        propertyHandle(entityBuild);
        entityBuild.addInnerClass(enumBuild(entityBuild));

        String fileCode = entityBuild.toString();
        String filepath = MasterMake.getMakeDir(makeInfo.getModuleName()) + File.separator + makeInfo.getClassname() + "Entity.java";

        message = MasterMake.writeFile(filepath, fileCode);

        return this;
    }

    private void propertyHandle(ClassBuild entityBuild) {
        PropertyBuild serialVersionUID = new PropertyBuild("serialVersionUID", long.class);
        serialVersionUID.setFinal(true);
        serialVersionUID.setStatic(true);
        serialVersionUID.setDefaultValue("1L");
        serialVersionUID.addAnnotationClassName(new AnnotationBuild(Serial.class));
        entityBuild.addImportClass(Serial.class);
        entityBuild.addProperty(serialVersionUID);

        ArrayList<String> commonProperty = new ArrayList<>() {{
            add(MasterMake.CREATE_AT);
            add(MasterMake.UPDATE_AT);
            add(MasterMake.DELETE_AT);
            add(MasterMake.PRIMARY);
        }};
        makeInfo.getColumns().forEach(tableColumn -> {
            if (commonProperty.contains(tableColumn.columnName())) {
                return;
            }

            String type;
            if (enumMap.containsKey(tableColumn.columnName())) {
                type = makeInfo.getPackageName() + "." + MasterMake.getClassName(tableColumn.columnName());
            } else if (tableColumn.columnType().contains("char") || tableColumn.columnType().contains("text") || tableColumn.columnType().contains("blob")) {
                type = String.class.getCanonicalName();
            } else if (tableColumn.columnType().contains("tinyint")) {
                type = Integer.class.getCanonicalName();
            } else if (tableColumn.columnType().contains("int")) {
                type = Long.class.getCanonicalName();
            } else if (tableColumn.columnType().contains("decimal") || tableColumn.columnType().contains("float") || tableColumn.columnType().contains("double")) {
                type = Float.class.getCanonicalName();
            } else if (tableColumn.columnType().contains("time")) {
                type = LocalDateTime.class.getCanonicalName();
            } else if (tableColumn.columnType().contains("date")) {
                type = LocalDate.class.getCanonicalName();
            } else {
                type = String.class.getCanonicalName();
            }
            int lastIndexOf = type.lastIndexOf('.');
            String importClassname = type.substring(lastIndexOf + 1);
            String importClassPackage = type.substring(0, lastIndexOf);

            if (!importClassPackage.equals(makeInfo.getPackageName())) {
                entityBuild.addImportClass(type);
            }

            if (importClassname.equals(MasterMake.getClassnameByTableName(makeInfo.getTableName())) && !importClassPackage.equals(makeInfo.getPackageName())){
                importClassname = type;
            }

            PropertyBuild property = new PropertyBuild(MasterMake.getPropertyName(tableColumn.columnName()), new GenericTypeBuild(importClassname));
            property.setDocument(new String[]{tableColumn.columnComment()});
            entityBuild.addProperty(property);
        });
    }

    public ClassBuild[] enumBuild(ClassBuild entityBuild) {
        ArrayList<ClassBuild> enums = new ArrayList<>();

        enumMap.forEach((key, value) -> {
            String classname = MasterMake.getClassName(key);
            ClassBuild classBuild = new ClassBuild(classname, ClassType.ENUM);
            StringBuilder columnComment = new StringBuilder();
            makeInfo.getColumns().forEach(column -> {
                if (column.columnName().equals(key)) {
                    columnComment.append(column.columnComment());
                }
            });

            classBuild.setDocument(new String[]{makeInfo.getTableComment() + " " + columnComment});
            entityBuild.addImportClass(IEnum.class);
            classBuild.addImplementClass(IEnum.class);
            value.forEach((k, v) -> {
                classBuild.addEnumValue(
                        new EnumValueBuild(translation.get(v).toUpperCase().replace(' ', '_'))
                                .addValue(k)
                                .addValue(v)
                );
            });
            PropertyBuild valueProperty = new PropertyBuild("value", Integer.class);
            valueProperty.setFinal(true);
            valueProperty.addAnnotationClassName(new AnnotationBuild(EnumValue.class));
            classBuild.addProperty(valueProperty);
            classBuild.addProperty(new PropertyBuild("desc", String.class).setFinal(true));
            entityBuild.addImportClass(EnumValue.class);
            entityBuild.addImportClass(Getter.class);
            entityBuild.addImportClass(AllArgsConstructor.class);
            classBuild.addAnnotationClass(new AnnotationBuild(Getter.class));
            classBuild.addAnnotationClass(new AnnotationBuild(AllArgsConstructor.class));

            entityBuild.addImportClass(Map.class);
            entityBuild.addImportClass(List.class);

            GenericTypeBuild returnType = new GenericTypeBuild(List.class);
            returnType.addGeneric(new GenericTypeBuild(Map.class).addGeneric(String.class, Object.class));

            MethodBuild getEnumValuesMethod = new MethodBuild("getEnumList", returnType);
            getEnumValuesMethod.addCode(String.format("return IEnum.getEnumList(%s.values());", classname));
            getEnumValuesMethod.setStatic(true);
            classBuild.addMethod(getEnumValuesMethod);

            enums.add(classBuild);
        });

        return enums.toArray(new ClassBuild[0]);
    }
}
